package org.zt.java.dal.router.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zt.java.dal.config.DalConfig;
import org.zt.java.dal.config.vo.MethodInfo;
import org.zt.java.dal.config.vo.MethodRouterConfig;
import org.zt.java.dal.context.ContextHolder;
import org.zt.java.dal.datasource.DataSourceType;
import org.zt.java.dal.datasource.DynamicDataSource;
import org.zt.java.dal.loadbalance.SlaveLoadBalance;
import org.zt.java.dal.router.DataSourceRouter;
import org.zt.java.dal.util.DalConfigUtil;


public class DefaultDataSourceRouter implements DataSourceRouter{

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	private DalConfig dalConfig;
	private SlaveLoadBalance slaveLoadBalanceBean;

	public DefaultDataSourceRouter(DalConfig dalConfig, SlaveLoadBalance slaveLoadBalanceBean) {
		this.dalConfig = dalConfig;
		this.slaveLoadBalanceBean = slaveLoadBalanceBean;
	}

	@Override
	public String selectCurrentDataSourceKey() {
		//--------------------------判断走主库----------------------------
		if (isOuterTx()) {
			logger.debug("最外层方法：{} 已{}，走{}", getFirstMethodName(), "开启事务", "Master");
			return DynamicDataSource.MASTER_NAME;
		}

		if (isOuterSlaveAndInnerTx()) {
			logger.warn("最外层方法{}设置为Slave，内部方法{}开启了事务，走Master。可能存在逻辑或配置错误。请检查！", getFirstMethodName(),
					getCurrentMethodName());
			return DynamicDataSource.MASTER_NAME;
		}

		if (isGlobalTx()) {
			logger.debug("方法：{} 已{}，走{}", getCurrentMethodName(), "开启事务", "Master");
			return DynamicDataSource.MASTER_NAME;
		}

		if (isOuterMaster()) {
			logger.debug("最外层方法：{} 已{}，走{}", getFirstMethodName(), "标识Master", "Master");
			return DynamicDataSource.MASTER_NAME;
		}

		if (isOuterSlaveAndInnerMaster()) {
			logger.warn("最外层方法{}设置为Slave，内部方法{}设置了Master，走Master。可能存在逻辑或配置错误。请检查！", getFirstMethodName(),
					getCurrentMethodName());
			return DynamicDataSource.MASTER_NAME;
		}
		
		if (isMaster()) {
			logger.debug("方法：{} 已{}，走{}", getCurrentMethodName(), "标识Master", "Master");
			return DynamicDataSource.MASTER_NAME;
		}

		//--------------------------判断走从库----------------------------
		if (isOuterSlaveAndInnerDefault()) {
			logger.debug("最外层方法{}设置为Slave，内部方法{}未定义，走{}", getFirstMethodName(),
					getCurrentMethodName(), "Slave");
			return slaveLoadBalanceBean.selectSlaveDataSourceKey();
		}
		
		if (isSlave()) {
			logger.debug("方法：{} 已{}，走{}", getCurrentMethodName(), "标识Slave", "Slave");
			return slaveLoadBalanceBean.selectSlaveDataSourceKey();
		}
		
		//--------------------------判断走默认库（默认为主库）----------------------------
		if (getCurrentMethodName() != null) {// 加非空判断,避免首次启动时,打空方法的log
			// 如果上面都走空，则使用bean中配置的DefaultDataSource，此时直接返回null或任意不存在的数据源即可
			logger.debug("方法：{} 没开启事务且未定义主从配置，走{}", getCurrentMethodName(), "Master");
		}

		return null;
	}

	private boolean isOuterSlaveAndInnerTx() {
		MethodInfo firstMethodInfo = ContextHolder.getFirstMethodInfo();
		// 外从内事务
		if (firstMethodInfo!=null && DataSourceType.Slave.equals(firstMethodInfo.getUse())
				&& ContextHolder.isGlobalTransactional()) {
			return true;
		}

		return false;
	}

	private boolean isOuterSlaveAndInnerMaster() {
		MethodInfo firstMethodInfo = ContextHolder.getFirstMethodInfo();
		// 外从内主
		if (firstMethodInfo!=null && DataSourceType.Slave.equals(firstMethodInfo.getUse()) && isMaster()) {
			return true;
		}

		return false;
	}
	
	private boolean isOuterSlaveAndInnerDefault() {
		MethodInfo firstMethodInfo = ContextHolder.getFirstMethodInfo();
		MethodInfo currentMethodInfo = ContextHolder.getCurrentMethodInfo();
		// 外层slave，内层方法未定义路由
		if (firstMethodInfo != null && DataSourceType.Slave.equals(firstMethodInfo.getUse())
				&& !isExistMethodRouteInDalConfig(currentMethodInfo)) {
			return true;
		}

		return false;
	}

	private boolean isOuterTx() {
		MethodInfo firstMethodInfo = ContextHolder.getFirstMethodInfo();
		//外层开启事务
		if (firstMethodInfo!= null && Boolean.TRUE.equals(firstMethodInfo.getTx())) {
			return true;
		}
		return false;
	}

	private boolean isGlobalTx() {
		//线程内有开启过事务
		if (ContextHolder.isGlobalTransactional()) {
			return true;
		}
		return false;
	}

	private boolean isOuterMaster() {
		MethodInfo firstMethodInfo = ContextHolder.getFirstMethodInfo();
		if (firstMethodInfo != null) {
			MethodRouterConfig methodRouterConfig = DalConfigUtil.getMethodRouteConfig(dalConfig,
					firstMethodInfo.getClassName(), firstMethodInfo.getMethodName(),
					firstMethodInfo.getMethodParamType());
			//外主
			if (methodRouterConfig != null && DataSourceType.Master.equals(methodRouterConfig.getUse())) {
				return true;
			}
		}
		return false;
	}

	private boolean isMaster() {
		// 当前方法配置了主
		if (hasMethodRouteInDalConfig(ContextHolder.getCurrentMethodInfo(), DataSourceType.Master)) {
			return true;
		}

		return false;
	}

	private boolean isSlave() {
		// 当前方法配置了从
		if (hasMethodRouteInDalConfig(ContextHolder.getCurrentMethodInfo(), DataSourceType.Slave)) {
			return true;
		}

		return false;
	}

	private boolean hasMethodRouteInDalConfig(MethodInfo methodInfo, DataSourceType use) {
		if(methodInfo!=null){
			MethodRouterConfig methodRouterConfig = DalConfigUtil.getMethodRouteConfig(
					dalConfig, methodInfo.getClassName(),
					methodInfo.getMethodName(), methodInfo.getMethodParamType());
			if (methodRouterConfig != null && use.equals(methodRouterConfig.getUse())) {
				return true;
			}
		}
		
		return false;
	}

	private boolean isExistMethodRouteInDalConfig(MethodInfo methodInfo) {
		if (methodInfo != null) {
			MethodRouterConfig methodRouterConfig = DalConfigUtil.getMethodRouteConfig(dalConfig,
					methodInfo.getClassName(), methodInfo.getMethodName(), methodInfo.getMethodParamType());
			if (methodRouterConfig != null) {
				return true;
			}
		}

		return false;
	}
	

	private String getCurrentMethodName() {
		MethodInfo methodInfo = ContextHolder.getCurrentMethodInfo();
		if (methodInfo != null) {
			return methodInfo.getSimpleMethodName();
		}
		return null;
	}

	private String getFirstMethodName() {
		MethodInfo methodInfo = ContextHolder.getFirstMethodInfo();
		if (methodInfo != null) {
			return methodInfo.getSimpleMethodName();
		}
		return null;
	}


}
